import tkinter as tk
from tkinter import ttk, scrolledtext, filedialog, messagebox
import requests
import json
import threading
import time
from datetime import datetime

class HttpTesterApp:
    def __init__(self, root):
        self.root = root
        self.root.title("HTTP测试工具")
        self.root.geometry("900x600")
        self.root.minsize(800, 500)
        
        # 设置中文字体支持
        self.setup_fonts()
        
        # 创建主布局
        self.create_widgets()
        
        # 设置默认值
        self.url_entry.insert(0, "http://localhost:8080")
        
        # 上次请求开始时间
        self.last_request_time = None
    
    def setup_fonts(self):
        # 在Windows上设置合适的字体
        self.default_font = ("SimHei", 10)
        self.header_font = ("SimHei", 12, "bold")
        self.monospace_font = ("Courier New", 10)
    
    def create_widgets(self):
        # 创建主框架
        main_frame = ttk.Frame(self.root, padding="10")
        main_frame.pack(fill=tk.BOTH, expand=True)
        
        # URL和方法选择区域
        url_frame = ttk.LabelFrame(main_frame, text="请求设置", padding="8")
        url_frame.pack(fill=tk.X, pady=(0, 10))
        
        # 方法选择
        self.method_var = tk.StringVar(value="GET")
        method_frame = ttk.Frame(url_frame)
        method_frame.pack(side=tk.LEFT, padx=(0, 10))
        
        for method in ["GET", "POST", "OPTIONS"]:
            ttk.Radiobutton(method_frame, text=method, variable=self.method_var, 
                            value=method, command=self.on_method_change).pack(side=tk.LEFT, padx=5)
        
        # URL输入
        url_label = ttk.Label(url_frame, text="URL:")
        url_label.pack(side=tk.LEFT, padx=(10, 5))
        
        self.url_entry = ttk.Entry(url_frame)
        self.url_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 10))
        
        # 发送按钮
        send_button = ttk.Button(url_frame, text="发送请求", command=self.send_request)
        send_button.pack(side=tk.RIGHT, padx=5)
        
        # 清空按钮
        clear_button = ttk.Button(url_frame, text="清空", command=self.clear_all)
        clear_button.pack(side=tk.RIGHT)
        
        # 中间区域：请求头和请求体
        mid_frame = ttk.Frame(main_frame)
        mid_frame.pack(fill=tk.BOTH, expand=True, pady=(0, 10))
        
        # 请求头区域
        headers_frame = ttk.LabelFrame(mid_frame, text="请求头", padding="8")
        headers_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=(0, 5))
        
        headers_label = ttk.Label(headers_frame, text="格式: Header-Name: Value")
        headers_label.pack(anchor=tk.W, pady=(0, 5))
        
        self.headers_text = scrolledtext.ScrolledText(headers_frame, height=5, font=self.monospace_font)
        self.headers_text.pack(fill=tk.BOTH, expand=True)
        
        # 默认添加Content-Type
        self.headers_text.insert(tk.END, "Content-Type: application/json")
        
        # 请求体区域
        body_frame = ttk.LabelFrame(mid_frame, text="请求体 (POST)", padding="8")
        body_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=(5, 0))
        
        # POST类型选择
        post_type_frame = ttk.Frame(body_frame)
        post_type_frame.pack(fill=tk.X, pady=(0, 5))
        
        self.post_type_var = tk.StringVar(value="json")
        ttk.Radiobutton(post_type_frame, text="JSON数据", variable=self.post_type_var, 
                        value="json").pack(side=tk.LEFT, padx=5)
        ttk.Radiobutton(post_type_frame, text="表单数据", variable=self.post_type_var, 
                        value="form").pack(side=tk.LEFT, padx=5)
        
        # 文件上传按钮
        self.upload_var = tk.BooleanVar()
        upload_check = ttk.Checkbutton(post_type_frame, text="文件上传", variable=self.upload_var, 
                                       command=self.on_upload_toggle)
        upload_check.pack(side=tk.RIGHT, padx=5)
        
        # 文件路径选择
        self.file_frame = ttk.Frame(body_frame)
        self.file_frame.pack(fill=tk.X, pady=(0, 5))
        self.file_frame.pack_forget()  # 初始隐藏
        
        self.file_path_var = tk.StringVar()
        file_entry = ttk.Entry(self.file_frame, textvariable=self.file_path_var)
        file_entry.pack(side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 5))
        
        browse_button = ttk.Button(self.file_frame, text="浏览...", command=self.browse_file)
        browse_button.pack(side=tk.RIGHT)
        
        # 请求体文本框
        self.body_text = scrolledtext.ScrolledText(body_frame, height=5, font=self.monospace_font)
        self.body_text.pack(fill=tk.BOTH, expand=True)
        
        # 默认JSON示例
        self.body_text.insert(tk.END, "{\n    \"key\": \"value\"\n}")
        
        # 响应区域
        response_frame = ttk.LabelFrame(main_frame, text="响应", padding="8")
        response_frame.pack(fill=tk.BOTH, expand=True)
        
        # 响应状态和时间
        response_info_frame = ttk.Frame(response_frame)
        response_info_frame.pack(fill=tk.X, pady=(0, 5))
        
        self.status_var = tk.StringVar(value="等待请求...")
        status_label = ttk.Label(response_info_frame, textvariable=self.status_var, font=self.header_font)
        status_label.pack(side=tk.LEFT, padx=5)
        
        self.time_var = tk.StringVar(value="")
        time_label = ttk.Label(response_info_frame, textvariable=self.time_var)
        time_label.pack(side=tk.RIGHT, padx=5)
        
        # 响应头
        response_headers_label = ttk.Label(response_frame, text="响应头:")
        response_headers_label.pack(anchor=tk.W, pady=(0, 2))
        
        self.response_headers_text = scrolledtext.ScrolledText(response_frame, height=3, font=self.monospace_font)
        self.response_headers_text.pack(fill=tk.X, pady=(0, 5))
        self.response_headers_text.config(state=tk.DISABLED)
        
        # 响应体
        response_body_label = ttk.Label(response_frame, text="响应体:")
        response_body_label.pack(anchor=tk.W, pady=(0, 2))
        
        self.response_body_text = scrolledtext.ScrolledText(response_frame, height=10, font=self.monospace_font)
        self.response_body_text.pack(fill=tk.BOTH, expand=True)
        self.response_body_text.config(state=tk.DISABLED)
    
    def on_method_change(self):
        # 根据请求方法启用/禁用相关控件
        method = self.method_var.get()
        if method == "GET" or method == "OPTIONS":
            self.body_text.config(state=tk.DISABLED)
            for child in self.post_type_frame.winfo_children():
                child.config(state=tk.DISABLED)
            if self.file_frame.winfo_ismapped():
                self.file_frame.pack_forget()
        else:
            self.body_text.config(state=tk.NORMAL)
            for child in self.post_type_frame.winfo_children():
                if not isinstance(child, ttk.Checkbutton):  # 不禁用文件上传复选框
                    child.config(state=tk.NORMAL)
            if self.upload_var.get():
                self.file_frame.pack(fill=tk.X, pady=(0, 5))
    
    def on_upload_toggle(self):
        # 切换文件上传选项
        if self.upload_var.get():
            self.file_frame.pack(fill=tk.X, pady=(0, 5))
            self.body_text.config(state=tk.DISABLED)
        else:
            self.file_frame.pack_forget()
            if self.method_var.get() != "GET" and self.method_var.get() != "OPTIONS":
                self.body_text.config(state=tk.NORMAL)
    
    def browse_file(self):
        # 打开文件选择对话框
        file_path = filedialog.askopenfilename(title="选择上传文件")
        if file_path:
            self.file_path_var.set(file_path)
    
    def parse_headers(self):
        # 解析请求头文本
        headers = {}
        header_text = self.headers_text.get("1.0", tk.END).strip()
        if not header_text:
            return headers
        
        for line in header_text.splitlines():
            if ":" in line:
                parts = line.split(":", 1)
                if len(parts) == 2:
                    key = parts[0].strip()
                    value = parts[1].strip()
                    headers[key] = value
        
        return headers
    
    def parse_form_data(self):
        # 解析表单数据
        form_data = {}
        body_text = self.body_text.get("1.0", tk.END).strip()
        
        if not body_text:
            return form_data
        
        for line in body_text.splitlines():
            if "=" in line:
                parts = line.split("=", 1)
                if len(parts) == 2:
                    key = parts[0].strip()
                    value = parts[1].strip()
                    form_data[key] = value
        
        return form_data
    
    def send_request(self):
        # 在单独的线程中发送请求，避免UI冻结
        threading.Thread(target=self._send_request_thread).start()
    
    def _send_request_thread(self):
        # 记录请求开始时间
        self.last_request_time = time.time()
        
        # 更新UI状态
        self.root.after(0, self.update_ui_during_request, True)
        
        try:
            url = self.url_entry.get().strip()
            if not url:
                raise ValueError("URL不能为空")
            
            method = self.method_var.get()
            headers = self.parse_headers()
            
            # 根据请求方法和类型准备请求参数
            if method == "GET":
                response = requests.get(url, headers=headers, timeout=30)
            elif method == "OPTIONS":
                response = requests.options(url, headers=headers, timeout=30)
            elif method == "POST":
                if self.upload_var.get():
                    # 文件上传
                    file_path = self.file_path_var.get().strip()
                    if not file_path:
                        raise ValueError("请选择要上传的文件")
                    
                    files = {'file': open(file_path, 'rb')}
                    response = requests.post(url, headers=headers, files=files, timeout=30)
                else:
                    # 常规POST请求
                    post_type = self.post_type_var.get()
                    if post_type == "json":
                        # JSON数据
                        body_text = self.body_text.get("1.0", tk.END).strip()
                        if body_text:
                            try:
                                json_data = json.loads(body_text)
                                response = requests.post(url, headers=headers, json=json_data, timeout=30)
                            except json.JSONDecodeError:
                                raise ValueError("JSON格式错误")
                        else:
                            response = requests.post(url, headers=headers, timeout=30)
                    else:
                        # 表单数据
                        form_data = self.parse_form_data()
                        response = requests.post(url, headers=headers, data=form_data, timeout=30)
            else:
                raise ValueError(f"不支持的请求方法: {method}")
            
            # 计算请求时间
            request_time = time.time() - self.last_request_time
            
            # 更新响应UI
            self.root.after(0, self.update_response_ui, response, request_time)
            
        except Exception as e:
            # 处理异常
            self.root.after(0, self.handle_request_error, str(e))
        finally:
            # 恢复UI状态
            self.root.after(0, self.update_ui_during_request, False)
    
    def update_ui_during_request(self, is_requesting):
        # 更新UI状态
        if is_requesting:
            self.status_var.set("请求中...")
            self.url_entry.config(state=tk.DISABLED)
            for child in self.root.winfo_children():
                if isinstance(child, ttk.Button):
                    child.config(state=tk.DISABLED)
        else:
            self.url_entry.config(state=tk.NORMAL)
            for child in self.root.winfo_children():
                if isinstance(child, ttk.Button):
                    child.config(state=tk.NORMAL)
    
    def update_response_ui(self, response, request_time):
        # 更新响应信息
        status_text = f"状态码: {response.status_code} - {response.reason}"
        self.status_var.set(status_text)
        
        # 更新响应时间
        time_text = f"请求时间: {request_time:.3f} 秒"
        self.time_var.set(time_text)
        
        # 更新响应头
        self.response_headers_text.config(state=tk.NORMAL)
        self.response_headers_text.delete("1.0", tk.END)
        headers_str = "\n".join([f"{k}: {v}" for k, v in response.headers.items()])
        self.response_headers_text.insert(tk.END, headers_str)
        self.response_headers_text.config(state=tk.DISABLED)
        
        # 更新响应体
        self.response_body_text.config(state=tk.NORMAL)
        self.response_body_text.delete("1.0", tk.END)
        
        try:
            # 尝试格式化JSON响应
            json_data = response.json()
            pretty_json = json.dumps(json_data, indent=2, ensure_ascii=False)
            self.response_body_text.insert(tk.END, pretty_json)
        except:
            # 非JSON响应直接显示
            self.response_body_text.insert(tk.END, response.text)
        
        self.response_body_text.config(state=tk.DISABLED)
    
    def handle_request_error(self, error_message):
        # 处理请求错误
        self.status_var.set(f"错误: {error_message}")
        messagebox.showerror("请求错误", error_message)
    
    def clear_all(self):
        # 清空所有输入和响应
        self.headers_text.delete("1.0", tk.END)
        self.body_text.delete("1.0", tk.END)
        self.response_headers_text.config(state=tk.NORMAL)
        self.response_headers_text.delete("1.0", tk.END)
        self.response_headers_text.config(state=tk.DISABLED)
        self.response_body_text.config(state=tk.NORMAL)
        self.response_body_text.delete("1.0", tk.END)
        self.response_body_text.config(state=tk.DISABLED)
        self.status_var.set("等待请求...")
        self.time_var.set("")
        self.file_path_var.set("")
        if self.file_frame.winfo_ismapped():
            self.file_frame.pack_forget()
        self.upload_var.set(False)

if __name__ == "__main__":
    root = tk.Tk()
    app = HttpTesterApp(root)
    root.mainloop()